# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

#########
#
# Locate and configure the Thrift library.
# Defines the following variables:
#
#   THRIFT_INCLUDE_DIR - the include directory for thrift headers
#   THRIFT_SHARED_LIBRARY - path to thrift's shared library
#   THRIFT_STATIC_LIBRARY - path to thrift's static library
#   THRIFT_EXECUTABLE - the thrift compiler
#   THRIFT_FOUND - whether the Thrift library and executable has been found
#
#  ====================================================================
#  Example:
#
#   find_package(Thrift REQUIRED)
#   include_directories(${THRIFT_INCLUDE_DIR})
#
#   include_directories(${CMAKE_CURRENT_BINARY_DIR})
#   THRIFT_GENERATE_CPP(THRIFT_SRCS THRIFT_HDRS THRIFT_TGTS
#     [SOURCE_ROOT <root from which source is found>]
#     [BINARY_ROOT <root into which binaries are built>]
#     THRIFT_FILES foo.thrift)
#   add_executable(bar bar.cc ${THRIFT_SRCS} ${THRIFT_HDRS})
#   target_link_libraries(bar ${THRIFT_SHARED_LIBRARY})
#
#  ====================================================================
#
# THRIFT_GENERATE_CPP (public function)
#   SRCS = Variable to define with autogenerated
#          source files
#   HDRS = Variable to define with autogenerated
#          header files
#   TGTS = Variable to define with autogenerated
#          custom targets; if SRCS/HDRS need to be used in multiple
#          libraries, those libraries should depend on these targets
#          in order to "serialize" the thrift invocations
#   FB303 = Option which determines if the Thrift definitions depend on the
#           FB303 support library.
#  ====================================================================

function(THRIFT_GENERATE_CPP SRCS HDRS TGTS)
  if(NOT ARGN)
    message(SEND_ERROR "Error: THRIFT_GENERATE_CPP() called without any thrift files")
    return()
  endif(NOT ARGN)

  set(options FB303)
  set(one_value_args SOURCE_ROOT BINARY_ROOT)
  set(multi_value_args EXTRA_THRIFT_PATHS THRIFT_FILES)
  cmake_parse_arguments(ARG "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN})
  if(ARG_UNPARSED_ARGUMENTS)
    message(SEND_ERROR "Error: unrecognized arguments: ${ARG_UNPARSED_ARGUMENTS}")
  endif()

  set(${SRCS})
  set(${HDRS})
  set(${TGTS})

  set(EXTRA_THRIFT_PATH_ARGS)
  foreach(PP ${ARG_EXTRA_THRIFT_PATHS})
    set(EXTRA_THRIFT_PATH_ARGS ${EXTRA_THRIFT_PATH_ARGS} -I ${PP})
  endforeach()

  if("${ARG_SOURCE_ROOT}" STREQUAL "")
    SET(ARG_SOURCE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}")
  endif()
  GET_FILENAME_COMPONENT(ARG_SOURCE_ROOT ${ARG_SOURCE_ROOT} ABSOLUTE)

  if("${ARG_BINARY_ROOT}" STREQUAL "")
    SET(ARG_BINARY_ROOT "${CMAKE_CURRENT_BINARY_DIR}")
  endif()
  GET_FILENAME_COMPONENT(ARG_BINARY_ROOT ${ARG_BINARY_ROOT} ABSOLUTE)

  foreach(FIL ${ARG_THRIFT_FILES})
    get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
    get_filename_component(FIL_WE ${FIL} NAME_WE)

    set(THRIFT_H_OUT "${ARG_BINARY_ROOT}/${FIL_WE}_types.h" "${ARG_BINARY_ROOT}/${FIL_WE}_constants.h")
    set(THRIFT_CC_OUT "${ARG_BINARY_ROOT}/${FIL_WE}_constants.cpp" "${ARG_BINARY_ROOT}/${FIL_WE}_types.cpp")

    execute_process(COMMAND awk "/^service/ { print $2 }" "${ABS_FIL}"
                    OUTPUT_VARIABLE SERVICES
                    OUTPUT_STRIP_TRAILING_WHITESPACE)

    foreach(SERVICE ${SERVICES})
      list(APPEND THRIFT_H_OUT "${ARG_BINARY_ROOT}/${SERVICE}.h")
      list(APPEND THRIFT_CC_OUT "${ARG_BINARY_ROOT}/${SERVICE}.cpp")
    endforeach()

    list(APPEND ${SRCS} "${THRIFT_CC_OUT}")
    list(APPEND ${HDRS} "${THRIFT_H_OUT}")

    if(ARG_FB303)
      list(APPEND ${SRCS} fb303_types.cpp fb303_constants.cpp FacebookService.cpp)
      list(APPEND ${HDRS} fb303_types.h fb303_constants.h FacebookService.h)
    endif()

    add_custom_command(
      OUTPUT ${THRIFT_CC_OUT} ${THRIFT_H_OUT}
      DEPENDS ${ABS_FIL}
      COMMAND  ${THRIFT_EXECUTABLE}
      ARGS
        --gen cpp:moveable_types
        --recurse
        --out ${ARG_BINARY_ROOT}
        -I ${ARG_SOURCE_ROOT}
        # Used to find built-in .thrift files (e.g. fb303.thrift)
        -I ${THIRDPARTY_INSTALL_CURRENT_DIR}
        ${EXTRA_THRIFT_PATH_ARGS} ${ABS_FIL}
      COMMENT "Running C++ thrift compiler on ${FIL}"
      VERBATIM )

    # This custom target enforces that there's just one invocation of thrift
    # when there are multiple consumers of the generated files. The target name
    # must be unique; adding parts of the filename helps ensure this.
    string(MAKE_C_IDENTIFIER "${ARG_BINARY_ROOT}${FIL}" TGT_NAME)
    add_custom_target(${TGT_NAME}
      DEPENDS "${THRIFT_CC_OUT}" "${THRIFT_H_OUT}")
    list(APPEND ${TGTS} "${TGT_NAME}")
  endforeach()

  set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE)
  set(${SRCS} ${${SRCS}} PARENT_SCOPE)
  set(${HDRS} ${${HDRS}} PARENT_SCOPE)
  set(${TGTS} ${${TGTS}} PARENT_SCOPE)
endfunction()

find_path(THRIFT_INCLUDE_DIR thrift/Thrift.h
  NO_CMAKE_SYSTEM_PATH
  NO_SYSTEM_ENVIRONMENT_PATH)

find_library(THRIFT_SHARED_LIBRARY thrift
             DOC "The Thrift Library"
             NO_CMAKE_SYSTEM_PATH
             NO_SYSTEM_ENVIRONMENT_PATH)

find_library(THRIFT_STATIC_LIBRARY libthrift.a
  DOC "Static version of the Thrift Library"
  NO_CMAKE_SYSTEM_PATH
  NO_SYSTEM_ENVIRONMENT_PATH)

find_program(THRIFT_EXECUTABLE thrift
  DOC "The Thrift Compiler"
  NO_CMAKE_SYSTEM_PATH
  NO_SYSTEM_ENVIRONMENT_PATH)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(THRIFT REQUIRED_VARS
  THRIFT_SHARED_LIBRARY THRIFT_STATIC_LIBRARY
  THRIFT_INCLUDE_DIR THRIFT_EXECUTABLE)
